package com.ansion.utils;

import android.annotation.SuppressLint;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.DhcpInfo;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.telephony.TelephonyManager;
import android.text.TextUtils;

import androidx.annotation.NonNull;

import com.ansion.basetool.preconditions.Preconditions;
import com.ansion.groovy.GroovyArray;
import com.ansion.log.VLog;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.regex.Pattern;


/**
 * Description:
 * <p>
 * Created by ____lamer on 2019/3/28 5:55 PM
 */
public class NetworkUtils {
    private final static String TAG = "NetworkUtils";

    public static final String NET_OFF_ACTION = "action_network_off";
    public static final String NET_ON_ACTION = "action_network_on";
    public static final String EXTRA_NETWORK_TYPE = "extra_network_type";

    public static final int TYPE_UNKNOW = 0;
    public static final int TYPE_WIFI = 1;
    public static final int TYPE_2G = 2;
    public static final int TYPE_3G = 3;
    public static final int TYPE_4G = 4;
    public static final int TYPE_WIMAX = 5;

    public static class NetworkInfoBean {
        public InetAddress address;
        public boolean isHotPort;

        public NetworkInfoBean(InetAddress inetAddress, boolean isHotPort) {
            this.address = inetAddress;
            this.isHotPort = isHotPort;
        }
    }

    public static boolean hasNetwork(Context context) {
        ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (manager != null) {
            try {
                NetworkInfo info = manager.getActiveNetworkInfo();
                if (info == null || !info.isConnected()) {
                    return false;
                } else {
                    return true;
                }
            } catch (Throwable t) {
                return false;
            }
        }
        return false;
    }

    public static @NonNull String getNetworkState(Context context) {
        return hasNetwork(context) ? "Connected" : "Disconnected";
    }


    public static int getNetType(Context context) {
        String netType = getNetworkType(context);
        return getNetTypeValue(netType);
    }

    public static boolean is2GMode(Context context) {
        return getNetType(context) == TYPE_2G;
    }

    public static String getNetworkType(Context context) {
        String type = "UNKNOWN";
        try {
            ConnectivityManager connectivity = (ConnectivityManager) context
                    .getSystemService(Context.CONNECTIVITY_SERVICE);
            if (connectivity != null) {
                @SuppressLint("MissingPermission") NetworkInfo info = connectivity.getActiveNetworkInfo();
                if (info != null && info.isAvailable()
                        && info.getState() == NetworkInfo.State.CONNECTED) {
                    switch (info.getType()) {
                        case ConnectivityManager.TYPE_WIFI:
                            type = "WIFI";
                            break;
                        case ConnectivityManager.TYPE_MOBILE:
                            type = getNetworkSubType(info.getSubtype());
                            if (TextUtils.isEmpty(type)) {
                                type = "MOBILE";
                            }
                            break;
                        case ConnectivityManager.TYPE_MOBILE_DUN:
                            type = getNetworkSubType(info.getSubtype());
                            if (TextUtils.isEmpty(type)) {
                                type = "MOBILE_DUN";
                            }
                            break;
                        case ConnectivityManager.TYPE_MOBILE_HIPRI:
                            type = getNetworkSubType(info.getSubtype());
                            if (TextUtils.isEmpty(type)) {
                                type = "MOBILE_HIPRI";
                            }
                            break;
                        case ConnectivityManager.TYPE_MOBILE_MMS:
                            type = getNetworkSubType(info.getSubtype());
                            if (TextUtils.isEmpty(type)) {
                                type = "MOBILE_MMS";
                            }
                            break;
                        case ConnectivityManager.TYPE_MOBILE_SUPL:
                            type = getNetworkSubType(info.getSubtype());
                            if (TextUtils.isEmpty(type)) {
                                type = "MOBILE_SUPL";
                            }
                            break;
                        case ConnectivityManager.TYPE_WIMAX:
                            type = "WIMAX";
                            break;
                        case ConnectivityManager.TYPE_ETHERNET:
                            type = "ETHERNET";
                            break;
                        case ConnectivityManager.TYPE_BLUETOOTH:
                            type = "BLUETOOTH";
                            break;
                        case ConnectivityManager.TYPE_DUMMY:
                            type = "DUMMY";
                            break;
                        case ConnectivityManager.TYPE_VPN:
                            type = "VPN";
                            break;
                    }
                }
            }
        } catch (Exception e) {
        }
        return type;
    }


    public static String getNetworkTypeForMP(Context context) {
        String type = "unknown";
        try {
            ConnectivityManager connectivity = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            if (connectivity != null) {
                @SuppressLint("MissingPermission") NetworkInfo info = connectivity.getActiveNetworkInfo();
                if (info != null && info.isAvailable() && info.getState() == NetworkInfo.State.CONNECTED) {

                    if (info.getType() == ConnectivityManager.TYPE_WIFI) {
                        return "wifi";
                    } else if (info.getType() == ConnectivityManager.TYPE_MOBILE ||
                            info.getType() == ConnectivityManager.TYPE_MOBILE_DUN ||
                            info.getType() == ConnectivityManager.TYPE_MOBILE_HIPRI ||
                            info.getType() == ConnectivityManager.TYPE_MOBILE_MMS ||
                            info.getType() == ConnectivityManager.TYPE_MOBILE_SUPL) {

                        switch (info.getSubtype()) {
                            case TelephonyManager.NETWORK_TYPE_1xRTT:
                            case TelephonyManager.NETWORK_TYPE_CDMA:
                            case TelephonyManager.NETWORK_TYPE_EDGE:
                            case TelephonyManager.NETWORK_TYPE_GPRS:
                            case TelephonyManager.NETWORK_TYPE_IDEN:
                                return "2g";
                            case TelephonyManager.NETWORK_TYPE_EVDO_0:
                            case TelephonyManager.NETWORK_TYPE_EVDO_A:
                            case TelephonyManager.NETWORK_TYPE_EVDO_B:
                            case TelephonyManager.NETWORK_TYPE_HSDPA:
                            case TelephonyManager.NETWORK_TYPE_HSPAP:
                            case TelephonyManager.NETWORK_TYPE_HSPA:
                            case TelephonyManager.NETWORK_TYPE_HSUPA:
                            case TelephonyManager.NETWORK_TYPE_UMTS:
                            case TelephonyManager.NETWORK_TYPE_EHRPD:
                                return "3g";
                            case TelephonyManager.NETWORK_TYPE_LTE://4G
                                return "4g";
                            case TelephonyManager.NETWORK_TYPE_NR://4G
                                return "5g";
                        }

                    }
                } else {
                    return "none";
                }
            }
        } catch (Exception e) {
        }
        return type;
    }

    public static String getNetworkSubType(int subtype) {
        String type = "";
        switch (subtype) {
            case TelephonyManager.NETWORK_TYPE_1xRTT:
                type = "2G_1xRTT";
                break;
            case TelephonyManager.NETWORK_TYPE_CDMA:
                type = "2G_CDMA";
                break;
            case TelephonyManager.NETWORK_TYPE_EDGE:
                type = "2G_EDGE";
                break;
            case TelephonyManager.NETWORK_TYPE_GPRS:
                type = "2G_GPRS";
                break;
            case TelephonyManager.NETWORK_TYPE_IDEN:
                type = "2G_IDEN";
                break;
            case TelephonyManager.NETWORK_TYPE_EVDO_0:
                type = "3G_EVDO_0";
                break;
            case TelephonyManager.NETWORK_TYPE_EVDO_A:
                type = "3G_EVDO_A";
                break;
            case TelephonyManager.NETWORK_TYPE_EVDO_B:
                type = "3G_EVDO_B";
                break;
            case TelephonyManager.NETWORK_TYPE_HSDPA:
                type = "3G_HSDPA";
                break;
            case TelephonyManager.NETWORK_TYPE_HSPAP:
                type = "3G_HSPAP";
                break;
            case TelephonyManager.NETWORK_TYPE_HSPA:
                type = "3G_HSPA";
                break;
            case TelephonyManager.NETWORK_TYPE_HSUPA:
                type = "3G_HSUPA";
                break;
            case TelephonyManager.NETWORK_TYPE_UMTS:
                type = "3G_UMTS";
                break;
            case TelephonyManager.NETWORK_TYPE_UNKNOWN:
                type = "UNKNOWN";
                break;
            case TelephonyManager.NETWORK_TYPE_EHRPD:
                type = "3G_EHRPD";
                break;
            case TelephonyManager.NETWORK_TYPE_LTE:
                type = "4G_LTE";
                break;
            case TelephonyManager.NETWORK_TYPE_NR:
                type = "5G_NR";
                break;
            default:
                break;
        }
        return type;
    }

    private static int getNetTypeValue(String strNetType) {
        if (TextUtils.isEmpty(strNetType)) {
            return TYPE_UNKNOW;
        }

        if (strNetType.contains("WIFI")) {
            return TYPE_WIFI;
        }
        if (strNetType.contains("2G_")) {
            return TYPE_2G;
        }
        if (strNetType.contains("3G_")) {
            return TYPE_3G;
        }
        if (strNetType.contains("4G_")) {
            return TYPE_4G;
        }
        if (strNetType.contains("WIMAX")) {
            return TYPE_WIMAX;
        }

        if (strNetType.contains("MOBILE")) {
            return TYPE_3G;
        }

        return TYPE_UNKNOW;
    }

    //Requires the ACCESS_NETWORK_STATE permission.
    //support API : 16 <-> 29:
    public static boolean useProxy() {
        try {
            Enumeration<NetworkInterface> enumeration = NetworkInterface.getNetworkInterfaces();
            if (enumeration == null) {
                return false;
            }
            List<NetworkInterface> interfaceList = Collections.list(enumeration);
            if (GroovyArray.isEmpty(interfaceList)) {
                return false;
            }
            return GroovyArray.any(interfaceList, (index, it) -> {
                try {
                    //it.isUp maybe throw socket exception
                    if (it != null && it.isUp()) {
                        String name = it.getName();
                        return name.contains("tun") || name.contains("ppp") || name.contains("pptp");
                    }
                    return false;
                } catch (Throwable ignore) {
                    return false;
                }
            });
        } catch (Throwable ignore) {
            return false;
        }
    }


    public static String getIP() {
        try {
            for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
                NetworkInterface intf = en.nextElement();
                for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
                    InetAddress inetAddress = enumIpAddr.nextElement();
                    if (!inetAddress.isLoopbackAddress() && (inetAddress instanceof Inet4Address)) {
                        return inetAddress.getHostAddress();
                    }
                }
            }
        } catch (SocketException ex) {
            if (VLog.OPEN_LOG) {
                VLog.d(TAG, "getIP crashed \n %s", ex);
            }
        }
        return null;
    }


    public static boolean is2GNetwork() {
        return getNetworkType(ResourceUtils.getContext()).startsWith("2G_");
    }

    public static boolean isInWifi() {
        return getNetworkType(ResourceUtils.getContext()).equals("WIFI");
    }

    public final static boolean hasInternet() {
        ConnectivityManager manager = (ConnectivityManager) ResourceUtils
                .getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        if (manager != null) {
            NetworkInfo info = manager.getActiveNetworkInfo();
            if (info == null || !info.isConnected()) {
                return false;
            }
        }

        // if (info.isRoaming()) {
        // return true;
        // }
        return true;

    }


    public static boolean isDataRoaming() {
        ConnectivityManager manager = (ConnectivityManager) ResourceUtils.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo info = manager.getActiveNetworkInfo();
        if (info == null || !info.isConnected()) {
            return false;
        }
        return info.isRoaming();
    }


    /**
     * Ipv4 address check.
     */
    private static final Pattern IPV4_PATTERN = Pattern.compile(
            "^(" + "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}" +
                    "([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$");

    /**
     * Check if valid IPV4 address.
     *
     * @param input the address string to check for validity.
     * @return True if the input parameter is a valid IPv4 address.
     */
    public static boolean isIPv4Address(String input) {
        return IPV4_PATTERN.matcher(input).matches();
    }

    /**
     * Get local Ip address.
     */
    public static NetworkInfoBean getTransportIpAddress() {
        Preconditions.checkNonMainThread();
        Enumeration<NetworkInterface> enumeration = null;
        try {
            enumeration = NetworkInterface.getNetworkInterfaces();
        } catch (SocketException e) {
            e.printStackTrace();
        }
        List<InetAddress> addressList = new ArrayList<>();
        InetAddress wlan = null;
        InetAddress sim = null;
        InetAddress hotPort = null;
        String wifiAddress = getWifiAddress();
//        String simCardIp = getSIMCardIp();
        VLog.i(TAG, "getWifiAddress:%s", wifiAddress);
        if (enumeration != null) {
            while (enumeration.hasMoreElements()) {
                NetworkInterface nif = enumeration.nextElement();
                Enumeration<InetAddress> inetAddresses = nif.getInetAddresses();
                if (inetAddresses != null) {
                    while (inetAddresses.hasMoreElements()) {
                        InetAddress inetAddress = inetAddresses.nextElement();
                        if (!inetAddress.isLoopbackAddress() && isIPv4Address(inetAddress.getHostAddress())) {
                            VLog.i(TAG, "name:[%s,%s],ip:%s", inetAddress.getCanonicalHostName(), inetAddress.getHostName(), inetAddress.getHostAddress());
                            String hostAddress = inetAddress.getHostAddress();
                            if (TextUtils.equals(wifiAddress, hostAddress)) {
                                wlan = inetAddress;
                            }
//                            else if (TextUtils.equals(simCardIp, hostAddress)) {
//                                sim = inetAddress;
//                            }
                            else if (hostAddress.startsWith("192.168")) {
                                hotPort = inetAddress;
                            }
                            addressList.add(inetAddress);
                        }
                    }
                }
            }
        }
//        if (wlan != null) {
//            addressList.remove(wlan);
//        }
//        if (sim != null) {
//            addressList.remove(sim);
//        }
//        if (addressList.size() == 1) {//刚好剩余1个
//            return new NetworkInfoBean(addressList.get(0), wlan != null || sim != null);
//        }
        if (hotPort != null && !TextUtils.equals(hotPort.getHostAddress(), wifiAddress)) {
            //连接wifi，又打开热点，又打开手机流量的时候，sim取出来是空的
            return new NetworkInfoBean(hotPort, true);
        }
        if (wlan != null)
            return new NetworkInfoBean(wlan, false);
        return null;
    }

    public static String getWifiAddress() {
        String wifiIp = null;
        // 获取WiFi服务
        WifiManager wifiManager = (WifiManager) ResourceUtils.getContext().getApplicationContext().getSystemService(Context.WIFI_SERVICE);
        // 判断WiFi是否开启
        if (wifiManager.isWifiEnabled()) {
            // 已经开启了WiFi
            WifiInfo wifiInfo = wifiManager.getConnectionInfo();
            if (wifiInfo != null) {
                int ipAddress = wifiInfo.getIpAddress();
                wifiIp = intToIp(ipAddress);
                VLog.i(TAG, "getWifiAddress:%s", wifiIp);
            }
        }
        return wifiIp;
    }

    private static String intToIp(int ipAddress) {
        return (ipAddress & 0xFF) + "." +
                ((ipAddress >> 8) & 0xFF) + "." +
                ((ipAddress >> 16) & 0xFF) + "." +
                (ipAddress >> 24 & 0xFF);
    }

    /**
     * 获取子网掩码
     *
     * @return
     */
    private static String getSubnetMask(String ipv4) {
        try {
            Enumeration<NetworkInterface> enumeration = NetworkInterface.getNetworkInterfaces();
            if (enumeration != null) {
                while (enumeration.hasMoreElements()) {
                    NetworkInterface networkInterface = (NetworkInterface) enumeration.nextElement();
                    if (!networkInterface.isUp()) {
                        continue;
                    }
                    for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) {
                        if (interfaceAddress.getAddress() instanceof Inet4Address && TextUtils.equals(ipv4, interfaceAddress.getAddress().getHostAddress())) {
                            return calcMaskByPrefixLength(interfaceAddress.getNetworkPrefixLength());
                        }
                    }
                }
            }
        } catch (SocketException e) {
            e.printStackTrace();
        }
        return "get-error";
    }

    private static String calcMaskByPrefixLength(int length) {

        int mask = 0xffffffff << (32 - length);
        int partsNum = 4;
        int bitsOfPart = 8;
        int[] maskParts = new int[partsNum];
        int selector = 0x000000ff;
        for (int i = 0; i < maskParts.length; i++) {
            int pos = maskParts.length - 1 - i;
            maskParts[pos] = (mask >> (i * bitsOfPart)) & selector;
        }
        String result = "";
        result = result + maskParts[0];
        for (int i = 1; i < maskParts.length; i++) {
            result = result + "." + maskParts[i];
        }
        return result;
    }


    private static int stringToInt(String addrString) {
        try {
            if (addrString == null)
                return 0;

            String[] parts = addrString.split("\\.");

            if (parts.length != 4) {
                return 0;
            }

            int a = Integer.parseInt(parts[0]);

            int b = Integer.parseInt(parts[1]) << 8;

            int c = Integer.parseInt(parts[2]) << 16;

            int d = Integer.parseInt(parts[3]) << 24;

            return a | b | c | d;

        } catch (NumberFormatException ex) {
            return 0;
        }
    }

    /***
     * 比较局域网， ip和掩码 与运算
     * 分别拿到两台设备的ip地址(int类型的ip)和子网掩码(int类型的netMask),然后得到两台设备 ip and netMask 的值，如果相同就是在同一个局域网内，否则就不是。
     * @return
     */
    public static int ipAddressAndNetMask(String ipV4) {
        WifiManager wifiManager = (WifiManager) ResourceUtils.getContext().getApplicationContext().getSystemService(Context.WIFI_SERVICE);
        DhcpInfo dhcpInfo = wifiManager.getDhcpInfo();
        int ipAddress = dhcpInfo.ipAddress;
        int netMask = dhcpInfo.netmask;
        if (netMask == 0) {
            String wlan0Mask = getWlan0Mask();
            if (defaultIP.equals(wlan0Mask) || default255.equals(wlan0Mask)) {
                wlan0Mask = getSubnetMask(ipV4);
            }
            netMask = stringToInt(wlan0Mask);
        }
        if (netMask == 255 || netMask == 0) {
            return 0;
        }
        return ipAddress & netMask;
    }


    /***
     * 需要注意的是，只有不连接wifi只使用4G的时候，才能取到；当连接wifi的时候即便插卡取到的也是null
     * @return
     */
    public static String getSIMCardIp() {
        ConnectivityManager connectivity = (ConnectivityManager) ResourceUtils.getContext()
                .getSystemService(Context.CONNECTIVITY_SERVICE);
        @SuppressLint("MissingPermission") NetworkInfo info = connectivity.getActiveNetworkInfo();
        @SuppressLint("MissingPermission") Network[] networks = connectivity.getAllNetworks();
        for (Network network : networks) {
            NetworkCapabilities networkCapabilities =
                    connectivity.getNetworkCapabilities(network);
            if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
                VLog.d(TAG, "当前在使用数据网络上网");
                return getLocalNetworkAddress(connectivity, network);
            }
        }
        return null;
    }


    /**
     * 获取名称、ip
     *
     * @return
     */
    private static String getLocalNetworkAddress(ConnectivityManager cm, Network net) {
        VLog.d(TAG, "getLocalNetworkAddress");
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (net == null) {
                return null;
            }
            LinkProperties linkProps = cm.getLinkProperties(net);
            if (linkProps == null) {
                return null;
            }
            String ipv4 = null;
            for (LinkAddress addr : linkProps.getLinkAddresses()) {
                VLog.d(TAG, "hostaddress: %s", addr.getAddress().getHostAddress());
                InetAddress a = addr.getAddress();
                if (a instanceof Inet4Address) {
                    if (!a.isLinkLocalAddress()) {
                        ipv4 = a.getHostAddress();
                    }
                }
            }
            return ipv4;
        }
        return null;
    }

    /**
     * 判断是否同一局域网
     *
     * @param ipAndNetMask
     * @return
     */
    public static boolean isSameLAN(String ipAndNetMask) {
        if (ipAndNetMask == null || "0".equals(ipAndNetMask)) {
            return true;
        }
        VLog.i(TAG, "isSameLAN:%s", ipAndNetMask);
        //只要新设备开了热点，无论是否连接wifi，都无法正确拿到子网掩码，所以就不需要校验局
        NetworkInfoBean bean = getTransportIpAddress();
        if (bean == null)
            return false;
        if (bean.isHotPort)//开热点的时候直接返回true
            return true;
        int localIpAndNetMask = ipAddressAndNetMask(bean.address.getHostAddress());
        if (localIpAndNetMask == 0) {
            return true;
        }
        return ipAndNetMask.equals(String.valueOf(localIpAndNetMask));
    }


    private static final String defaultIP = "0.0.0.0";
    private static final String default255 = "255.0.0.0";
    private static final String command = "ifconfig";

    /**
     * 无线网卡
     *
     * @return
     */
    public static String getWlan0Mask() {
        String Mask = defaultIP;
        try {
            Process process = Runtime.getRuntime().exec(new String[]{command});
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            do {
                String line = bufferedReader.readLine();
                if (line == null) {
                    break;
                }
                if (line.startsWith("wlan0     ")) {
                    Mask = defaultIP;
                }
                Mask = getLineMask(line).equals("") ? Mask : getLineMask(line);
                if (line.startsWith("p2p0      ")) {
                    break;
                }


            } while (true);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Mask;
    }

    /**
     * 有线网卡
     *
     * @return
     */
    public static String getEth0Mask() {
        String Mask = defaultIP;
        try {
            Process process = Runtime.getRuntime().exec(new String[]{command});
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            do {
                String line = bufferedReader.readLine();
                if (line == null) {
                    break;
                }
                if (line.startsWith("eth0      ")) {
                    Mask = defaultIP;
                }
                Mask = getLineMask(line).equals("") ? Mask : getLineMask(line);
                if (line.startsWith("lo        ")) {
                    break;
                }
            } while (true);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Mask;
    }

    private static String getLineMask(String line) {
        String IP = "";
        if (line.trim().matches("inet addr:(\\d{1,3}\\.){3}\\d{1,3}( ){2}" +
                "(Bcast:(\\d{1,3}\\.){3}\\d{1,3}( ){2}){0,1}" +
                "Mask:(\\d{1,3}\\.){3}\\d{1,3}")) {
            String[] props = line.trim().split("( ){2}");
            for (String prop : props) {
                if (prop.length() == 0) {
                    continue;
                }

                String[] kv = prop.split(":");
                if (kv[0].startsWith("inet addr")) {
                } else if (kv[0].startsWith("Bcast")) {
                } else if (kv[0].startsWith("Mask")) {
                    IP = kv[1];
                }
            }
        }
        return IP;
    }
}

